package com.zeng.ssm.service.support.idempotent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanCreationNotAllowedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Properties;

/**
 * 分布式锁DistributedLockService的PostProcessor
 * <p>
 * 注册DistributedLockService和KeyIdempotentAspect、ParamIdempotentAspect
 *
 * @author zengwanlin
 * @date 2022-01-20T14:53
 */
@Component
public class DistributedLockPostProcessor implements BeanFactoryPostProcessor {

    private final Logger logger = LoggerFactory.getLogger(DistributedLockPostProcessor.class);

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;

        final String memcached = "memcached";
        final String redis = "redis";
        final String database = "database";

        // Spring beanName
        final String beanName = "distributedLockService";
        final String memCachedClient = "memCachedClient";
        final String redisTemplate = "redisTemplate";

        final String lockTypeKey = "switch.distributedLock.type";
        final String lockHolderKey = "switch.distributedLock.holder";

        if (defaultListableBeanFactory.containsBean(beanName)) {
            logger.debug("已存在beanName[{}]实例", beanName);
            return;
        }

        Properties properties = defaultListableBeanFactory.getBean("configProperties", Properties.class);
        String lockType = properties.getProperty(lockTypeKey);

        if (memcached.equals(lockType)) {
            if (!defaultListableBeanFactory.containsBean(memCachedClient)) {
                throw new NoSuchBeanDefinitionException(MessageFormatter.format("Can not find the instance of beanName [{}]，pls check the properties for sure", memCachedClient).getMessage());
            }
            // 注册分布式锁
            defaultListableBeanFactory.registerBeanDefinition(beanName, getDefaultBeanDefinition(MemcachedDistributedLockServiceImpl.class));
        } else if (redis.equals(lockType)) {
            if (!defaultListableBeanFactory.containsBean(redisTemplate)) {
                String[] redisTemplateNameArr = defaultListableBeanFactory.getBeanNamesForType(RedisTemplate.class, Boolean.FALSE, Boolean.TRUE);
                if (redisTemplateNameArr == null || redisTemplateNameArr.length <= 0) {
                    throw new NoSuchBeanDefinitionException(MessageFormatter.format("Can not find [{}] from Spring IOC，pls check the properties for sure", redisTemplate).getMessage());
                }
                if (redisTemplateNameArr.length != 1) {
                    throw new BeanCreationNotAllowedException(beanName, MessageFormatter.format("Too much instance of [{}] in Spring IOC，expected 1", redisTemplateNameArr.length).getMessage());
                }
                defaultListableBeanFactory.registerAlias(redisTemplateNameArr[0], redisTemplate);
            }
            // 注册分布式锁
            defaultListableBeanFactory.registerBeanDefinition(beanName, getDefaultBeanDefinition(RedisDistributedLockServiceImpl.class));
        } else if (database.equals(lockType)) {
            String lockHolder = properties.getProperty(lockHolderKey);
            if (!StringUtils.hasText(lockHolder)) {
                throw new BeanCreationNotAllowedException(beanName, MessageFormatter.format("Property [{}] can not be null，pls check the properties for sure", lockHolderKey).getMessage());
            }
            // 注册分布式锁
            RootBeanDefinition beanDefinition = getDefaultBeanDefinition(DbDistributedLockServiceImpl.class);
            MutablePropertyValues propertyValues = new MutablePropertyValues();
            propertyValues.addPropertyValue("holder", lockHolder);
            beanDefinition.setPropertyValues(propertyValues);
            defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
        } else {
            // 未知的缓存类型
            throw new NoSuchBeanDefinitionException(MessageFormatter.format("Unsupported lock type [{}]，pls check the property [{}] for sure", lockType, lockTypeKey).getMessage());
        }
    }

    private RootBeanDefinition getDefaultBeanDefinition(Class<?> beanClass) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON);
        beanDefinition.setAutowireCandidate(true);
        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
        beanDefinition.setBeanClass(beanClass);
        return beanDefinition;
    }

}
